<?php

namespace Nece\Sdk\Coze;

use Firebase\JWT\JWT;
use Psr\SimpleCache\CacheInterface;
use Throwable;

class AccessToken
{
    private $api_uri = '/api/permission/oauth2/token';
    private $aud = 'api.coze.cn';
    private $client_id;
    private $client_secret;
    private $private_key;
    private $http_client;
    private $cacher;
    private $cache_key;

    /**
     * 构造函数
     *
     * @author nece001@163.com
     * @create 2025-04-22 20:18:06
     *
     * @param string $client_id 应用ID
     * @param string $client_secret 公钥指纹
     * @param string $private_key 私钥
     * @param object $http_client HTTP客户端
     * @param CacheInterface|null $cacher 缓存接口
     */
    public function __construct(string $client_id, string $client_secret, string $private_key, $http_client, ?CacheInterface $cacher = null)
    {
        $this->cache_key = '__coze_access_token_' . $client_id . '__';
        $this->client_id = $client_id;
        $this->client_secret = $client_secret;
        $this->private_key = $private_key;
        $this->http_client = $http_client;
        $this->cacher = $cacher;
    }

    /**
     * 获取access_token（有缓存的）
     *
     * @author gjw
     * @created 2025-04-23 10:29:13
     *
     * @return array [access_token, expires_in, token_type,...]
     */
    public function getToken()
    {
        $token = $this->getCache();
        if (!$token) {
            $token = $this->getAccessToken();
            $this->setCache($token);
        }
        return $token;
    }

    /**
     * 获取access_token
     * 
     * @return array [access_token, expires_in, token_type, ...]
     * @throws CozeException
     */
    public function getAccessToken()
    {
        $jwt = $this->generateJWT();
        try {
            $response = $this->http_client->post($this->api_uri, [
                'form_params' => [
                    'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
                    'duration_seconds' => 86399,
                ],
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => 'Bearer ' . $jwt,
                ]
            ]);

            $body = $response->getBody()->getContents();
            $data = json_decode($body, true);
            if (isset($data['access_token'])) {
                return $data;
            } else {
                throw new CozeException('获取access_token失败: ' . $body);
            }
        } catch (Throwable $e) {
            throw new CozeException('请求Coze授权接口异常: ' . $e->getMessage());
        }
    }

    /**
     * 生成JWT
     * 
     * @return string JWT令牌
     */
    private function generateJWT()
    {
        $now = time();
        $payload = [
            'iss' => $this->client_id,
            'aud' => $this->aud,
            'iat' => $now,
            'exp' => $now + 300,
            'jti' => uniqid()
        ];
        return JWT::encode($payload, $this->private_key, 'RS256', $this->client_secret);
    }

    /**
     * 设置缓存
     * 
     * @param array $token 令牌数据
     * @return void
     */
    private function setCache(array $token)
    {
        if ($this->cacher) {
            $ttl = $token['expires_in'] - time();
            $this->cacher->set($this->cache_key, $token, $ttl);
        } else {
            // 创建临时文件存储
            $tmp = sys_get_temp_dir() . '/' . $this->cache_key . '.tmp';
            file_put_contents($tmp, json_encode($token));
        }
    }

    /**
     * 获取缓存
     * 
     * @return array|null 缓存的令牌数据或null
     */
    private function getCache()
    {
        if ($this->cacher) {
            return $this->cacher->get($this->cache_key);
        } else {
            $tmp = sys_get_temp_dir() . '/' . $this->cache_key . '.tmp';
            if (file_exists($tmp)) {
                $data = json_decode(file_get_contents($tmp), true);
                if (isset($data['expires_in']) && $data['expires_in'] > time()) {
                    return $data;
                }
            }
        }
        return null;
    }
}
